#!/usr/bin/env python 
# -*- coding: utf-8 -*-
# @Time    : 2023/4/12 21:57
# @Author  : WJQ
# @Site    : 
# @File    : Node.py
# @Software: PyCharm
from typing import List, Optional, Dict, Union
from appium.webdriver.webelement import WebElement


class Component:
    x: int
    y: int
    width: int
    height: int
    label: str
    method: str
    tagName: str
    className: str
    visited: bool

    def __init__(self, value: Union[WebElement, Dict], method='click'):
        if isinstance(value, WebElement) :
            self.tagName = value.tag_name if value.tag_name else ""
            self.className = value.tag_name if value.tag_name else ""
            rect = value.rect
            text = value.text.replace('\n', '')[:10]
        else :
            self.tagName = value['tagName']
            self.className = value['className']
            rect = value['rect']
            text = value['text'].replace('\n', '')[:10]

        # self.x = int(rect['x'] + (rect['width'] // 2))
        # self.y = int(rect['y'] + (rect['height'] // 2))
        self.x = int(rect['x'])
        self.y = int(rect['y'])
        self.width = int(rect['width'])
        self.height = int(rect['height'])
        self.label = text
        self.method = method
        self.visited = False

    def __repr__(self):
        return f"label: {self.label}, className: {self.className}, tagName: {self.tagName}, x: {self.x}, y: {self.y}, visit: {self.visited}"

    def __eq__(self, other):
        if not isinstance(other, Component):
            return False
        return self.label == other.label or (abs(self.x - other.x) < 80 and abs(self.y - other.y) < 80)
        # if self.label == '' and other.label == '':
        #     return abs(self.x-other.x) < 60 and abs(self.y-other.y) < 60
        # return self.label == other.label

    def __hash__(self):
        return hash((self.x, self.y))

    def merge ( self, other ) :
        # Merge this component with another component
        self.x = other.x
        self.y = other.y
        self.label = (self.label + other.label)[:10]  # Concatenate and limit to 10 characters


class Node:
    path: str
    query: str
    components: List[Component]
    node_id: int
    scroll_number: int
    repeat_number: int
    children: List['Node']
    parent: Optional['Node']
    visited: bool
    state: bool

    def __init__( self, path: str, query: str , components: List[Component], node_id: int = 0):
        self.path = path
        self.query = query
        self.node_id = node_id
        # self.components = [Component(component) for component in components if
        #                    not self._handle_exception(Component, component)]
        self.components = components
        self.scroll_number = 0
        self.repeat_number = 0
        self.children = list()
        self.parent = None
        self.visited = False
        self.state = True

    def _handle_exception(self, constructor, component):
        try:
            constructor(component)
            return False
        except Exception as e:
            return True

    @staticmethod
    def are_nodes_similar(node1: 'Node', node2: 'Node', threshold = 0.8) -> bool:
        if node1.path != node2.path:
            return False

        common = 0
        for comp1 in node1.components:
            for comp2 in node2.components:
                if comp1 == comp2:
                    common += 1
                    break

        total = len(node1.components) + len(node2.components) - common
        if total == 0:
            return True
        return common / total >= threshold

    def __repr__(self):
        return f"Node: {self.path} 's id is {self.node_id} with " \
               f"{len(self.components)} components"

    def __eq__(self, other):
        return self.node_id == other.node_id

    def __hash__(self):
        return hash(self.path + self.query + len(self.components).__str__() + self.node_id.__str__())
